﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Send2SMTP
{
    partial class Macro
    {
        delegate string MacroHandler(MailThreadData threadData,Random rand, string[] args);

        //
        // 'Subject' macros
        Dictionary<string, MacroHandler> m_subjectMacros = new Dictionary<string, MacroHandler>()
        {
            {"%MESSAGENUM",MessageNumber_Handler},
            {"%DATETIME",DateTime_Handler},
            {"%THREADID",ThreadId_Handler},
            {"%RANDOMTEXT",RandomText_Handler},
            {"%UNICODETABLE",UnicodeTable_Handler},
            {"%UNICODECHAR",UnicodeChar_Handler}
        };

        //
        // 'Body' macros
        Dictionary<string, MacroHandler> m_bodyMacros = new Dictionary<string, MacroHandler>()
        {
            {"%MESSAGENUM",MessageNumber_Handler},
            {"%DATETIME",DateTime_Handler},
            {"%THREADID",ThreadId_Handler},
            {"%RANDOMTEXT",RandomText_Handler},
            {"%UNICODETABLE",UnicodeTable_Handler},
            {"%UNICODECHAR",UnicodeChar_Handler}
        };

        public string ParseSubjectText(MailThreadData threadData, Random rand,string subjectText)
        {
            return ParseText(threadData, m_subjectMacros, rand,subjectText);
        }

        public string ParseBodyText(MailThreadData threadData, Random rand,string bodyText)
        {
            return ParseText(threadData, m_bodyMacros, rand,bodyText);
        }

        string ParseText(MailThreadData threadData, Dictionary<string,MacroHandler> handlers,Random rand,string inputText)
        {
            StringBuilder outputText = new StringBuilder();

            StringBuilder macroName = new StringBuilder();
            StringBuilder args = new StringBuilder();

            int cur = 0;
            bool inName = false;
            bool inArgs = false;
            while (cur < inputText.Length) 
            {
                switch (inputText[cur])
                {
                    case '%':
                        //
                        // Possibly a macro so start building up its name
                        //macroName.Remove(0, macroName.Length);
                        inName = true;
                        break;

                    case ' ':
                    case '\t':
                    case '\n':
                        if (inName)
                        {
                            //
                            // We potentially have a macro in the form %MACRO. Pass this to the evaluator
                            inName = false;
                            outputText.Append(EvaluateMacro(threadData, rand, handlers, macroName.ToString(), ""));
                            outputText.Append(inputText[cur]);

                            //
                            // We've consumed the current character so start from the top again
                            cur++;
                            macroName.Remove(0, macroName.Length);
                            args.Remove(0, args.Length);
                            continue;
                        }
                        break;


                    case '(':
                        if (inName)
                        {
                            //
                            // Possibly the start of macro arguments so start building up the argument string
                            inName = false;
                            args.Remove(0, args.Length);
                            inArgs = true;
                        }
                        break;
                    case ')':
                        if (inArgs)
                        {
                            //
                            // We potentially have a macro in the form %MACRO(a,b,c). Pass this onto evaluator.
                            inArgs = false;
                            args.Append(')');
                            outputText.Append(EvaluateMacro(threadData, rand, handlers, macroName.ToString(), args.ToString()));

                            //
                            // We've consumed the current character so start from the top again
                            cur++;
                            macroName.Remove(0, macroName.Length);
                            args.Remove(0, args.Length);
                            continue;
                        }
                        break;

                }

                //
                // If we make it this far we're still building either the macro name, arguments or the output text...
                if (inName)
                {
                    macroName.Append(inputText[cur]);
                }
                else if (inArgs)
                {
                    args.Append(inputText[cur]);
                }
                else
                {
                    outputText.Append(inputText[cur]);
                }
                cur++;
            }

            //
            // If we are still building up the macro name, try and evaluate it now..
            if (inName)
            {
                outputText.Append(EvaluateMacro(threadData, rand, handlers, macroName.ToString(), ""));
            }
            else if (inArgs)
            {
                //
                // If we are still in args then the macro didn't have a closing ')'. So just append the text
                outputText.Append(macroName.ToString());
                outputText.Append(args.ToString());
            }



            return outputText.ToString();
        }

        static char[] argDelims = new char[] {','};
        string EvaluateMacro(MailThreadData threadData, Random rand, Dictionary<string,MacroHandler> handlers,string macroName,string param)
        {
            if (handlers.ContainsKey(macroName))
            {
                if (param.Length > 2)
                {
                    return handlers[macroName](threadData, rand, param.Substring(1, param.Length - 2).Split(argDelims));
                }
                return handlers[macroName](threadData,rand,new string[0]);
            }
            else
            {
                return macroName+param;
            }
        }
    }
}
